package com.inexas.cu.server.register;

import com.google.inject.*;
import com.inexas.cu.client.pipe.*;
import com.inexas.cu.client.register.*;
import com.inexas.cu.server.core.*;
import com.inexas.cu.server.pipe.*;
import com.inexas.cu.server.pipe.ServiceRegistry.*;
import com.inexas.cu.stratus.*;
import com.inexas.util.*;

public class AccountProvider implements ServiceProvider {
	private final Stratus stratus;

	@Inject
	private AccountProvider(PipeServerEnd pipeServerEnd, Stratus stratus) {
		this.stratus = stratus;

		// Tell the server end of the pipe which requests we will handle...
		pipeServerEnd.register(UserSessionRequest.class, this);
		pipeServerEnd.register(RegistrationRequest.class, this);
	}

	/**
	 * This method is essentially a switch that demultiplexes the incoming
	 * traffic from the pipe to a method that will handle each one
	 */
	public AbstractResponse service(AbstractRequest request) {
		// ?todo Can I use a switch here instead of if-else statements?
		final AbstractResponse result;
		if(request instanceof SignIn) {
			result = doSignIn((SignIn)request);
		} else if(request instanceof Register) {
			result = doCreateAccount((Register)request);
		} else if(request instanceof Deregister) {
			result = doDeleteAccount((Deregister)request);
		} else {
			throw new RuntimeException("Request not handled: " + request.getClass().getName());
		}
		return result;
	}

	private AbstractResponse doCreateAccount(Register request) {
		final Person person = new Person(request.getFirstName(), request.getLastName());
		stratus.create(person);

		final Account account = new Account(person, request.getAccountName(), request.getPassword());
		stratus.create(account);

		return new RegisterResult(request);
	}

	private AbstractResponse doSignIn(SignIn request) {
		final CurrentUser result;

		final String accountName = request.getAccountName();
		final String password = request.getPassword();

		if(StringUtilities.isEmpty(accountName) || StringUtilities.isEmpty(password)) {
			result = new CurrentUser(request, "Missing account name or password");
		} else {
			final Account account = stratus.findBy( //
			        Account.class, //
			        "name:\"" + accountName + '\"', "password:\"" + password + '\"');
			if(account == null) {
				result = new CurrentUser(request, "No account found");
			} else {
				final Person person = stratus.findByIdent(Person.class, account.getPersonIdent());
				if(person == null) {
					throw new RuntimeException("Concurrency error??");
				}
				result = new CurrentUser( //
				        request, //
				        account.getName(), //
				        person.getFirstName(), person.getLastName());
			}
		}
		return result;
	}

	private AbstractResponse doDeleteAccount(Deregister request) {
		final DeregisterResult result = new DeregisterResult(request);

		// Check the credentials of the user...
		final String credentials = request.getCredentials();
		if(credentials == null) {
			result.setErrorMessage( //
			        "The current user does not have the credentials to " + //
			                "delete an account: " + credentials);
		} else {
			final String accountName = request.getAccountName();
			final Account account = stratus.findBy( //
			        Account.class, //
			        "name:\"" + accountName + '\"');
			if(account == null) {
				result.setErrorMessage("Account does not exist");
			} else {
				final Person person = stratus.findByIdent(Person.class, account.getPersonIdent());
				if(person == null) {
					throw new RuntimeException("Concurrency error??");
				}

				stratus.delete(account);
				stratus.delete(person);
			}

		}

		return result;
	}

}
